package com.wonders.common.parse.jaxb;

import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.bind.ValidationEvent;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.validation.Schema;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.sun.xml.bind.marshaller.CharacterEscapeHandler;
import com.wonders.common.parse.jaxb.config.JaxbConfig;
import com.wonders.common.parse.jaxb.entity.vo.XsdConfig;
import com.wonders.common.parse.jaxb.handler.GreedyValidationEventHandler;
import com.wonders.framework.exception.FrameworkException;

/**
 * JAXB操作方法
 * 
 * @author WuBin
 * 
 */
@Component
public class JaxbService {

	@Autowired
	private JaxbConfig jaxbConfig;

	@Autowired
	private CharacterEscapeHandler characterEscapeHandler;

	/**
	 * 对象转xml
	 * 
	 * @param key
	 *            业务主键
	 * @param type
	 *            报文类型
	 * @param obj
	 *            对象
	 * @return xml
	 */
	public String toXml(String key, String type, Object obj) {
		return toXml(key, type, obj, "UTF-8");
	}

	/**
	 * 对象转xml
	 * 
	 * @param key
	 *            业务主键
	 * @param type
	 *            报文类型
	 * @param obj
	 *            对象
	 * @param encoding
	 *            编码
	 * @return xml
	 */
	public String toXml(String key, String type, Object obj, String encoding) {
		try {
			XsdConfig xsdConfig = jaxbConfig.getXsdConfig(key, type);
			JAXBContext jc = xsdConfig.getJaxbContext();
			Schema schema = xsdConfig.getSchema();
			Marshaller m = jc.createMarshaller();
			m.setSchema(schema);
			m.setProperty("jaxb.formatted.output", false);
			m.setProperty("com.sun.xml.bind.characterEscapeHandler",
					characterEscapeHandler);
			m.setEventHandler(new GreedyValidationEventHandler());
			StringWriter sw = new StringWriter();
			m.setProperty(Marshaller.JAXB_ENCODING, encoding);
			m.marshal(obj, sw);
			ValidationEventHandler veh = m.getEventHandler();
			validateXml(veh);
			return sw.toString();
		} catch (FrameworkException e) {
			throw new FrameworkException(StringUtils.join("object转换为xml失败,",
					e.getMessage(), ",obj:", obj.toString()));
		} catch (Exception e) {
			throw new FrameworkException("object转换为xml失败", e);
		}
	}

	/**
	 * xml转对象
	 * 
	 * @param key
	 *            业务主键
	 * @param type
	 *            报文类型
	 * @param xml
	 *            xml
	 * @return 对象
	 */
	public Object fromXML(String key, String type, String xml) {
		try {
			XsdConfig xsdConfig = jaxbConfig.getXsdConfig(key, type);
			JAXBContext jc = xsdConfig.getJaxbContext();
			Schema schema = xsdConfig.getSchema();
			Unmarshaller unmarshaller = jc.createUnmarshaller();
			unmarshaller.setSchema(schema);
			unmarshaller.setEventHandler(new GreedyValidationEventHandler());
			StringReader reader = new StringReader(xml);
			Object obj = unmarshaller.unmarshal(reader);
			ValidationEventHandler veh = unmarshaller.getEventHandler();
			validateXml(veh);
			return obj;
		} catch (FrameworkException e) {
			throw new FrameworkException(StringUtils.join("xml转换为object失败,",
					e.getMessage(), ",xml:", xml));
		} catch (Exception e) {
			throw new FrameworkException("xml转换为object失败", e);
		}
	}

	/**
	 * 校验xml
	 * 
	 * @param veh
	 *            检验的处理者
	 */
	private void validateXml(ValidationEventHandler veh) {
		if ((veh instanceof GreedyValidationEventHandler)) {
			GreedyValidationEventHandler gveh = (GreedyValidationEventHandler) veh;
			if (gveh.hasEvents()) {
				Map<String, String> validationMap = new HashMap<String, String>();
				for (int i = 0; i < gveh.getEvents().size(); i++) {
					ValidationEvent ve = (ValidationEvent) gveh.getEvents()
							.get(i);
					StringBuilder sb = new StringBuilder();
					sb.append(ve.getMessage());
					if (ve.getLocator() != null) {
						sb.append(" @[")
								.append(ve.getLocator().getLineNumber())
								.append(",")
								.append(ve.getLocator().getColumnNumber())
								.append("]");
					}
					validationMap.put("ERROR" + i, sb.toString());
				}
				throw new FrameworkException("xsd校验不通过："
						+ validationMap.toString());
			}
		}
	}

}
